/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.persistence.basic;

import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.util.Assert;
import org.proteusframework.platformservice.ServiceInitializationException;
import org.proteusframework.platformservice.lifecycle.api.*;
import org.proteusframework.platformservice.persistence.api.IProjectDescriptor;
import org.proteusframework.platformservice.persistence.api.IProjectManagerVisitor;
import org.proteusframework.platformservice.persistence.base.AbstractProjectManager;
import org.proteusframework.platformservice.persistence.project.NoSuchProjectException;
import org.proteusframework.platformservice.persistence.project.ProjectDescriptor;
import org.proteusframework.platformservice.persistence.project.ProjectInitializationException;
import org.proteusframework.platformservice.persistence.util.FileUtil;
import org.proteusframework.platformservice.theme.IThemeService;

import java.io.*;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

public final class DefaultProjectManager extends AbstractProjectManager
{
    private static final String KEY_LAST_PROJECT = "lastProject";

    private static final String PROJECT_FILE = ".project";

    private static final Logger logger = Logger.getLogger(DefaultProjectManager.class.getCanonicalName());

    private static final String PROPERTIES_SUFFIX = ".properties";

    private static final String HISTORY_PROPERTIES = ".history" + PROPERTIES_SUFFIX;

    private File historyProperties;

    private ILifecycleService lifecycleService;

    private boolean projectOpenFlag = false;

    private File currentProjectRoot;

    private String projectsDirectoryPath;

    private File projectsDirectory;

    protected void onDelegateInjected()
    {
        projectsDirectoryPath = getAppConfigService().getDefaultProjectsDirectory();

        projectsDirectory = new File(projectsDirectoryPath);
        logger.config("Default Projects Directory path defined as " + projectsDirectoryPath);

        historyProperties = new File(projectsDirectory, HISTORY_PROPERTIES);

        lifecycleService = getDelegate().getLifecycleService();
    }

    @Override
    public void create(IProjectDescriptor descriptor) throws ProjectInitializationException
    {
        Assert.parameterNotNull(descriptor, "Parameter 'descriptor' must not be null");
        Assert.parameterNotNull(descriptor.getId(), "Descriptor must provide a non-null project name in the ID field");

        boolean continueFlag = true;

        if (isProjectOpen())
        {
            continueFlag = closeCurrentProject(true, true);
        }

        if (this.doesProjectExist(descriptor))
        {
            throw new IllegalArgumentException("An existing project already exists in the specified location: " + descriptor.toString());
        }

        if (continueFlag)
        {
            IProjectDescriptor clone = ProjectDescriptor.cloneReadOnlyDescriptor(getOpenProject());

            LifecycleActivityEvent<IProjectDescriptor> preEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                    LifecycleContext.ProjectLifecycle,
                    LifecyclePhase.Pre,
                    LifecycleActivity.Create);

            lifecycleService.fireLifecycleEvent(preEvent);

            currentProjectRoot = new File(projectsDirectory, descriptor.getId());
            currentProjectRoot.mkdirs();

            logger.config("New project directory created at " + currentProjectRoot.getAbsolutePath());

            projectRegistration(descriptor);

            serializeProjectFile(descriptor);

            serializeProjectMetadata(descriptor);

            LifecycleActivityEvent<IProjectDescriptor> postEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                    LifecycleContext.ProjectLifecycle,
                    LifecyclePhase.Post,
                    LifecycleActivity.Create);

            lifecycleService.fireLifecycleEvent(postEvent);
        } else
        {
            logger.info("Create project cancelled; closeCurrentProject() returned false");
        }
    }

    @Override
    public void open(IProjectDescriptor descriptor) throws NoSuchProjectException, ProjectInitializationException
    {
        doOpen(descriptor, true);
    }

    @Override
    public void delete(IProjectDescriptor descriptor) throws NoSuchProjectException
    {
        Assert.parameterNotNull(descriptor, "Parameter 'descriptor' must not be null");
        Assert.parameterNotNull(descriptor.getId(), "Descriptor must provide a non-null project name in the ID field");

        int result = IThemeService.YES_OPTION;

        if (getDelegate().isThemeServiceAvailable())
        {

            result = getDelegate().getThemeService()
                    .showConfirmDialog("Permanently delete project " + descriptor.getId() + "?",
                            IThemeService.YES_NO_OPTION,
                            IThemeService.QUESTION_MESSAGE);
        }

        if (IThemeService.YES_OPTION == result)
        {
            if (null != getOpenProject() && getOpenProject().getId().equals(descriptor.getId()))
            {
                closeCurrentProject(true, false);

                projectOpenFlag = false;
                closeOpenProject();
                clearLastOpenedProjectPath();
            }


            if (doesProjectExist(descriptor))
            {
                IProjectDescriptor clone = ProjectDescriptor.cloneReadOnlyDescriptor(descriptor);

                LifecycleActivityEvent<IProjectDescriptor> preEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                        LifecycleContext.ProjectLifecycle,
                        LifecyclePhase.Pre,
                        LifecycleActivity.Delete);

                lifecycleService.fireLifecycleEvent(preEvent);

                try
                {
                    IProjectDescriptor lastOpened = getLastOpenedProject();
                    if (null != lastOpened && lastOpened.getId().equals(clone.getId()))
                    {
                        clearLastOpenedProjectPath();
                    }
                } catch (NoSuchProjectException ignore)
                {

                }

                currentProjectRoot = new File(projectsDirectory, descriptor.getId());
                boolean deleteSuccessFlag = FileUtil.deleteDirectory(currentProjectRoot);

                logger.info("Project " + clone.getId() + " deleted {successFlag = " + deleteSuccessFlag + "}");

                LifecycleActivityEvent<IProjectDescriptor> postEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                        LifecycleContext.ProjectLifecycle,
                        LifecyclePhase.Post,
                        LifecycleActivity.Delete);

                lifecycleService.fireLifecycleEvent(postEvent);
            } else
            {
                throw new NoSuchProjectException(descriptor);
            }
        }
    }

    @Override
    public boolean isProjectOpen()
    {
        return projectOpenFlag;
    }

    @Override
    public void save()
    {
        doSave(true);
    }

    @Override
    public void saveAs(IProjectDescriptor descriptor) throws ProjectInitializationException
    {
        Assert.parameterNotNull(descriptor, "Parameter 'descriptor' must not be null");
        Assert.parameterNotNull(descriptor.getId(), "Descriptor must provide a non-null project name in the ID field");

        if (isProjectOpen())
        {
            IProjectDescriptor clone = ProjectDescriptor.cloneReadOnlyDescriptor(getOpenProject());

            LifecycleActivityEvent<IProjectDescriptor> preEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                    LifecycleContext.ProjectLifecycle,
                    LifecyclePhase.Pre,
                    LifecycleActivity.SaveAs);

            lifecycleService.fireLifecycleEvent(preEvent);

            doSave(false);

            closeCurrentProject(false, false);

            File srcDir = new File(projectsDirectory, clone.getId());
            File dstDir = new File(projectsDirectory, descriptor.getId());
            try
            {
                FileUtil.copyDirectory(srcDir, dstDir);
            } catch (IOException e)
            {
                logger.severe("Unable to complete Save As operation: " + e.getMessage());
                e.printStackTrace();
            }

            try
            {
                doOpen(descriptor, false);
            } catch (NoSuchProjectException e)
            {
                logger.severe("Unable to open the new target of the Save As operation: " + e.getMessage());
            }

            LifecycleActivityEvent<IProjectDescriptor> postEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                    LifecycleContext.ProjectLifecycle,
                    LifecyclePhase.Post,
                    LifecycleActivity.SaveAs);

            lifecycleService.fireLifecycleEvent(postEvent);
        } else
        {
            logger.fine("IPersistenceService received a request to Save As a project; no project is open to Save As");
            throw new IllegalStateException("No project is open");
        }
    }

    @Override
    public void close()
    {
        if (isProjectOpen())
        {
            closeCurrentProject(true, true);
        } else
        {
            logger.fine("IPersistenceService received a request to Close a project; no project is open to Close");
            throw new IllegalStateException("No project is open");
        }
    }

    @Override
    public List<IProjectDescriptor> listProjects()
    {
        List<IProjectDescriptor> projects = new ArrayList<IProjectDescriptor>();

        if (projectsDirectory != null && projectsDirectory.isDirectory())
        {
            File dirs[] = projectsDirectory.listFiles(new FileFilter()
            {
                @Override
                public boolean accept(File file)
                {
                    return file.isDirectory();
                }
            });

            for (final File dir : dirs)
            {
                projects.add(createProjectDescriptor(dir));
            }
        }

        return projects;
    }

    @Override
    public IProjectDescriptor getLastOpenedProject() throws NoSuchProjectException
    {
        IProjectDescriptor projectDescriptor = null;

        if (historyProperties.exists())
        {
            Properties p = new Properties();
            InputStream is;
            try
            {
                is = new FileInputStream(historyProperties);
                p.load(is);

                String lastProjectName = p.getProperty(KEY_LAST_PROJECT);

                if (null != lastProjectName)
                {
                    File projectDir = new File(projectsDirectory, lastProjectName);

                    projectDescriptor = createProjectDescriptor(projectDir);
                }

            } catch (Exception e)
            {
                logger.warning("Unable to open " + HISTORY_PROPERTIES + " in " + projectsDirectoryPath);
            }
        }

        if (null == projectDescriptor)
        {
            throw new NoSuchProjectException(null);
        }

        return projectDescriptor;
    }

    @Override
    public void clearLastOpenedProjectPath()
    {
        if (null != historyProperties)
        {
            try
            {
                OutputStream os = new FileOutputStream(historyProperties);
                SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
                new Properties().store(os, "History reset on " + sdf.format(new Date()));
            } catch (Exception e)
            {
                logger.warning("Unable to reset " + HISTORY_PROPERTIES + " in " + projectsDirectoryPath);
            }
        }
    }

    @Override
    public boolean doesProjectExist(INamespace namespace)
    {
        Assert.parameterNotNull(namespace, "Parameter 'descriptor' must not be null");
        Assert.parameterNotNull(namespace.getId(), "Descriptor must provide a non-null project name");

        File f = new File(projectsDirectory, namespace.getId());
        return f.exists();
    }

    @Override
    public IProjectDescriptor loadProjectDescriptor(INamespace namespace) throws NoSuchProjectException
    {
        IProjectDescriptor descriptor;

        if (doesProjectExist(namespace))
        {
            descriptor = new ProjectDescriptor(namespace);
            deserializeProjectFile(descriptor);
            deserializeProjectMetadata(descriptor);
        } else
        {
            throw new NoSuchProjectException(namespace);
        }

        return descriptor;
    }

    @Override
    public boolean isValidProjectName(String projectName)
    {
        boolean isValid = true;
        try
        {
            //
            // If the project name specified can be used as a valid temp file prefix, then we know we have a valid
            // project name as far as the OS is concerned.
            File.createTempFile(projectName, "name-tests");
        } catch (IOException e)
        {
            isValid = false;
        }

        return isValid;
    }

    private IProjectDescriptor createProjectDescriptor(final File projectDir)
    {
        Assert.parameterNotNull(projectDir, "Parameter 'projectDir' must not be null");

        IProjectDescriptor projectDescriptor = new ProjectDescriptor(projectDir.getParentFile().getAbsolutePath(), projectDir.getName());

        deserializeProjectFile(projectDescriptor);
        deserializeProjectMetadata(projectDescriptor);

        return projectDescriptor;
    }

    private void doSave(boolean bubbleEventsFlag)
    {
        if (isProjectOpen())
        {
            IProjectDescriptor clone = ProjectDescriptor.cloneReadOnlyDescriptor(getOpenProject());

            if (bubbleEventsFlag)
            {
                LifecycleActivityEvent<IProjectDescriptor> preEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                        LifecycleContext.ProjectLifecycle,
                        LifecyclePhase.Pre,
                        LifecycleActivity.Save);

                lifecycleService.fireLifecycleEvent(preEvent);
            }


            serializeProjectFile(getOpenProject());

            serializeProjectMetadata(getOpenProject());

            if (bubbleEventsFlag)
            {
                LifecycleActivityEvent<IProjectDescriptor> postEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                        LifecycleContext.ProjectLifecycle,
                        LifecyclePhase.Post,
                        LifecycleActivity.Save);

                lifecycleService.fireLifecycleEvent(postEvent);
            }
        } else
        {
            logger.fine("IPersistenceService received a request to Save a project; no project is open to Save");
            throw new IllegalStateException("No project is open");
        }
    }

    private void doOpen(IProjectDescriptor descriptor, boolean bubbleEventsFlag) throws NoSuchProjectException, ServiceInitializationException
    {
        Assert.parameterNotNull(descriptor, "Parameter 'descriptor' must not be null");
        Assert.parameterNotNull(descriptor.getId(), "Descriptor must provide a non-null project name in the ID field");

        boolean continueFlag = true;

        if (isProjectOpen())
        {
            continueFlag = closeCurrentProject(true, true);
        }

        if (continueFlag)
        {
            if (doesProjectExist(descriptor))
            {
                // We have to load the project and metadata before the pre-open event so listeners know what is opening
                deserializeProjectFile(descriptor);
                deserializeProjectMetadata(descriptor);

                IProjectDescriptor clone = ProjectDescriptor.cloneReadOnlyDescriptor(getOpenProject());

                if (bubbleEventsFlag)
                {
                    LifecycleActivityEvent<IProjectDescriptor> preEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                            LifecycleContext.ProjectLifecycle,
                            LifecyclePhase.Pre,
                            LifecycleActivity.Open);

                    lifecycleService.fireLifecycleEvent(preEvent);
                }


                projectRegistration(descriptor);

                if (bubbleEventsFlag)
                {
                    LifecycleActivityEvent<IProjectDescriptor> postEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                            LifecycleContext.ProjectLifecycle,
                            LifecyclePhase.Post,
                            LifecycleActivity.Open);

                    lifecycleService.fireLifecycleEvent(postEvent);
                }
            } else
            {
                throw new NoSuchProjectException(descriptor);
            }
        }
    }

    protected void projectRegistration(IProjectDescriptor descriptor) throws ServiceInitializationException
    {
        projectOpenFlag = true;
        super.projectRegistration(descriptor);
        setCurrentProjectDescription(descriptor.getDescription());
        this.currentProjectRoot = new File(projectsDirectory, descriptor.getId());

        setHistory(descriptor.getId());

        // Prepare for the next project open by setting up a new, empty message bean manager
        DefaultPersistenceService dps = (DefaultPersistenceService) getDelegate().getPersistenceService();

        dps.createVFSManager(false);
        dps.createMessageBeanManager(false);
        dps.createPackageManager(false);

        //
        // This lifecycle event gives the app notification that it is time to inject its known and supported
        // message bean metadata into the IPersistService so that we can dynamically create the persistence structure
        //
        LifecycleActivityEvent<IProjectDescriptor> preRegEvent = new LifecycleActivityEvent<IProjectDescriptor>(descriptor,
                LifecycleContext.ProjectLifecycle,
                LifecyclePhase.Pre,
                LifecycleActivity.MessageBeanRegistration);

        lifecycleService.fireLifecycleEvent(preRegEvent);

        //
        // Open up the DB connection and create the database
        DefaultMessageBeanManager mbr = getDelegate()
                .getPersistenceService()
                .narrow(DefaultMessageBeanManager.class);

        mbr.openProjectDataStore(descriptor.getId());

        //
        // Now that the database is up and running, persist all of the classes that were declared and their
        // known properties for easy query via the DB without relying on reflection
        mbr.processRegistrationQueue();


        // Finally, notify all the listeners that message bean registration is complete and the database is online. This
        // is a listener's opportunity to insert pre-population data into the database or verify data existence.
        LifecycleActivityEvent<IProjectDescriptor> postRegEvent = new LifecycleActivityEvent<IProjectDescriptor>(descriptor,
                LifecycleContext.ProjectLifecycle,
                LifecyclePhase.Post,
                LifecycleActivity.MessageBeanRegistration);

        lifecycleService.fireLifecycleEvent(postRegEvent);

        //
        // Now we shift focus to the VFS. The pre-population gives them notice that we are about to setup a VFS for the
        // project. In general, there shouldn't be much for the listener to do since there is no VFS at this point in
        // time configured.
        LifecycleActivityEvent<IProjectDescriptor> prePoplEvent = new LifecycleActivityEvent<IProjectDescriptor>(descriptor,
                LifecycleContext.ProjectLifecycle,
                LifecyclePhase.Pre,
                LifecycleActivity.VFSInitialization);

        lifecycleService.fireLifecycleEvent(prePoplEvent);


        // Create all the directories for the VFS system
        DefaultVFSManager vfsManager = getDelegate()
                .getPersistenceService()
                .narrow(DefaultVFSManager.class);

        vfsManager.initializeVirtualFileSystem(currentProjectRoot);

        //
        // Finally, give the listeners notice that the VFS is now online, and now would be a great time to copy in
        // any template files or to pre-stage files within the VFS.
        LifecycleActivityEvent<IProjectDescriptor> postPoplEvent = new LifecycleActivityEvent<IProjectDescriptor>(descriptor,
                LifecycleContext.ProjectLifecycle,
                LifecyclePhase.Post,
                LifecycleActivity.VFSInitialization);

        lifecycleService.fireLifecycleEvent(postPoplEvent);
    }

    private void deserializeProjectFile(IProjectDescriptor descriptor)
    {
        DataInputStream is = null;
        try
        {
            File myProjectDir = new File(projectsDirectory, descriptor.getId());
            File projectFile = new File(myProjectDir, PROJECT_FILE);

            is = new DataInputStream(
                    new BufferedInputStream(
                            new FileInputStream(projectFile)));

            is.readUTF();   // Family - already set
            is.readUTF();   // ID - already set
            is.readUTF();   // RefID - unused
            is.readUTF();   // Name - same as ID
            ((ProjectDescriptor) descriptor).setDescription(is.readUTF());

            try
            {
                //noinspection InfiniteLoopStatement
                while (true)
                {
                    String groupKey = is.readUTF();
                    ((ProjectDescriptor) descriptor).addGroupKey(groupKey);
                }
            } catch (EOFException ignore)
            {

            }

        } catch (IOException e)
        {
            logger.severe("Unable to read project descriptor: " + e.getMessage());
        } finally
        {
            if (null != is)
            {
                try
                {
                    is.close();
                } catch (IOException ignore)
                {

                }
            }
        }
    }

    private void serializeProjectFile(IProjectDescriptor descriptor)
    {
        DataOutputStream os = null;
        try
        {
            File myProjectDir = new File(projectsDirectory, getOpenProject().getId());
            File projectFile = new File(myProjectDir, PROJECT_FILE);

            os = new DataOutputStream(
                    new BufferedOutputStream(
                            new FileOutputStream(projectFile)));

            os.writeUTF(descriptor.getFamily());
            os.writeUTF(descriptor.getId());
            os.writeUTF((null == descriptor.getRefId()) ? "" : descriptor.getRefId());
            os.writeUTF(descriptor.getName());
            os.writeUTF((null == descriptor.getDescription()) ? "" : descriptor.getDescription());
            for (String key : descriptor.listPropertyGroups())
            {
                os.writeUTF(key);
            }

        } catch (IOException e)
        {
            logger.severe("Unable to write project descriptor: " + e.getMessage());
        } finally
        {
            if (null != os)
            {
                try
                {
                    os.flush();
                } catch (IOException ignore)
                {

                }

                try
                {
                    os.close();
                } catch (IOException ignore)
                {

                }
            }
        }
    }

    private void deserializeProjectMetadata(IProjectDescriptor descriptor)
    {
        List<String> groupKeys = descriptor.listPropertyGroups();
        for (String key : groupKeys)
        {
            Properties groupProps = descriptor.getProperties(key);
            if (null != groupProps)
            {
                try
                {
                    File groupPropertiesFile = new File(projectsDirectory, createGroupPropertyFileName(key));
                    InputStream is = new FileInputStream(groupPropertiesFile);
                    groupProps.load(is);
                } catch (Exception e)
                {
                    logger.warning("Unable to restore group properties " + key + " in " + projectsDirectoryPath + ": " + e.getMessage());
                }
            }
        }
    }

    private String createGroupPropertyFileName(String key)
    {
        return "." + key + PROPERTIES_SUFFIX;
    }

    private void serializeProjectMetadata(IProjectDescriptor descriptor)
    {
        List<String> groupKeys = descriptor.listPropertyGroups();
        for (String key : groupKeys)
        {
            Properties groupProps = descriptor.getProperties(key);
            if (null != groupProps)
            {
                try
                {
                    File groupPropertiesFile = new File(projectsDirectory, createGroupPropertyFileName(key));
                    OutputStream os = new FileOutputStream(groupPropertiesFile);

                    SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
                    groupProps.store(os, "Projected metadata last saved on " + sdf.format(new Date()));
                } catch (Exception e)
                {
                    logger.warning("Unable to store group properties " + key + " in " + projectsDirectoryPath + ": " + e.getMessage());
                }
            }
        }
    }

    private void setHistory(String name)
    {
        Properties p = new Properties();
        p.setProperty(KEY_LAST_PROJECT, name);

        try
        {
            OutputStream os = new FileOutputStream(historyProperties);

            SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
            p.store(os, "Projected accessed on " + sdf.format(new Date()));

        } catch (Exception e)
        {
            logger.warning("Unable to open " + HISTORY_PROPERTIES + " in " + projectsDirectoryPath);
        }
    }

    private boolean closeCurrentProject(boolean bubbleEventsFlag, boolean interactiveFlag)
    {
        // NOTE: Assume that -1 = cancel, 0 = Don't Save, 1 = Save
        int result;

        IProjectDescriptor clone = ProjectDescriptor.cloneReadOnlyDescriptor(getOpenProject());

        if (interactiveFlag && getDelegate().isThemeServiceAvailable())
        {
            result = getDelegate().getThemeService()
                    .showConfirmDialog("Save " + clone.getId() + " before continuing?",
                            IThemeService.YES_NO_CANCEL_OPTION,
                            IThemeService.QUESTION_MESSAGE);

            switch (result)
            {
                case IThemeService.CANCEL_OPTION:
                    // Cancel action
                    return false;
                case IThemeService.NO_OPTION:
                    // Don't Save
                    break;
                case IThemeService.YES_OPTION:
                    // Save
                    doSave(bubbleEventsFlag);
                    break;
            }
        } else
        {
            // No theme service available- so just do the save and bubble the events as requested
            doSave(bubbleEventsFlag);
        }

        logger.fine("Closing project " + clone.getId());

        if (bubbleEventsFlag)
        {
            LifecycleActivityEvent<IProjectDescriptor> preEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                    LifecycleContext.ProjectLifecycle,
                    LifecyclePhase.Pre,
                    LifecycleActivity.Close);

            lifecycleService.fireLifecycleEvent(preEvent);
        }


        logger.fine("Shut down IMessageBeanRegistrar for project " + clone.getId());

        closeOpenProject();

        projectOpenFlag = false;

        try
        {
            DefaultMessageBeanManager registrar = getDelegate().getPersistenceService()
                    .narrow(DefaultMessageBeanManager.class);
            registrar.shutdownDriver();
        } catch (SQLException e)
        {
            logger.severe("Unable to properly shutdown the DB driver: " + e.getMessage());
            e.printStackTrace();
        }

        //
        // Inject a null IMessageBeanRegistrar so that the post-close event cannot access the messageBeanManager
        DefaultPersistenceService dps = (DefaultPersistenceService) getDelegate().getPersistenceService();
        dps.createMessageBeanManager(true);
        dps.createVFSManager(true);
        dps.createPackageManager(true);

        if (bubbleEventsFlag)
        {
            LifecycleActivityEvent<IProjectDescriptor> postEvent = new LifecycleActivityEvent<IProjectDescriptor>(clone,
                    LifecycleContext.ProjectLifecycle,
                    LifecyclePhase.Post,
                    LifecycleActivity.Close);

            lifecycleService.fireLifecycleEvent(postEvent);
        }

        logger.fine(clone.getId() + " is now closed");

        // Current project was closed
        return true;
    }

    @Override
    public void accept(IProjectManagerVisitor visitor) throws IOException
    {
        visitor.visit(this);
    }
}
